// 2025/4/21: 首个版本
// 2025/4/30: 增加globalObjectsSampleConverters

var DynamicObjectType = {
    Unknown: 0,
    Car: 1, // 轿车
    Van: 2, // 面包车
    Truck: 3, // 货车
    Trailer: 4, // 拖车
    Semitrailer: 5, // 货柜车
    Bus: 6, // 大巴
    Motorbike: 7, // 摩托车
    Bicycle: 8, // 自行车
    Train: 9, // 火车
    Tram: 10, // 有轨电车
    Pedestrian: 11, // 行人
    Wheelchair: 12, // 轮椅
    Animal: 13, // 动物
};

function GlobalObjectColor() {
    this.valid = false;  // Whether the color is valid / 颜色是否有效
    this.r = 0;  // Red component of the object's color / 目标物的颜色R分量
    this.g = 0;  // Green component of the object's color / 目标物的颜色g分量
    this.b = 0;  // Blue component of the object's color / 目标物的颜色b分量
};

function DynamicObject() {
    this.id = 0; // ID of the object, 0 as subject, 1~ as other objects / 目标物ID，0表示为主目标物，1~表示一般目标物
    this.type = DynamicObjectType.Unknown; // Classification of the object / 目标物类型
    this.centerLongitude = 0.0; // [°E] Longitude of the object's box center / 目标物中心的经度
    this.centerLatitude = 0.0; // [°N] Latitude of the object's box center / 目标物中心的纬度，北为正方向
    this.centerAltitude = null; // [m] Altitude of the object's box center / 目标物中心的海拔
    this.orientation = null; // [deg] Heading of the object's box, 0 as north, CCW is positive / 目标物朝向，北为0，逆时针为正方向
    this.width = null; // [m] Width of the object's box / 目标物宽度
    this.length = null; // [m] Length of the object's box / 目标物长度
    this.height = null; // [m] Height of the object's box / 目标物高度
    this.speed = null; // [KPH] Speed of the object's box along the heading / 目标物速度
    this.accelX = null; // [m/s²] Acceleration of the object's box along the heading / 目标物纵向加速度
    this.curvature = null; // [1/m] Turn curvature of the object's rear axis center / 目标物转弯曲率
    this.yawRate = null; // [deg/s] Yaw rate of the object's box / 目标物横摆角速度
    this.pitch = null; // [deg] Pitch angle of the object's box, heading down is positive / 目标物俯仰角，车头朝下为正
    this.roll = null; // [deg] Roll angle of the object's box, right side heading down is positive / 目标物横滚角，右侧朝下为正
    this.rearAxisLongitudinalOffset = null; // [m] Longitudinal distance between the object's box center and rear axis center, always positive / 目标物自身坐标系下，后轴中心与物体中心的纵向距离，恒为正
    this.rearAxisHeightOffset = null; // [m] Height difference of the object's box center and rear axis center, always positive / 目标物自身坐标系下，后轴中心与物体中心的高度差，恒为正
    this.color = null; // Color of the object / 目标物颜色
}

function GlobalObjectsSample(session, time) {
    this.session = session; // Date or AppoxSessionIdentifier
    this.time = time; // [s] Time offset in session / Session内的相对时间
    this.objects = []; // List of objects / 目标物列表
}

function convGlobalObjectsSampleV1(gs) {
    if (gs.values.length < 1) return null;

    var objectCount = gs.values[0] == null ? 0 : gs.values[0];
    if (gs.values.length != 1 + 10 * objectCount) return null;

    var sample = new GlobalObjectsSample(gs.session, gs.time);
    sample.objects = Array(objectCount);

    for (var i = 0; i < objectCount; i++) {
        var obj = new DynamicObject();
        var baseIndex = 1 + 10 * i;
        obj.id = gs.values[baseIndex + 0];
        if (gs.values[baseIndex + 1] != null) obj.type = gs.values[baseIndex + 1];
        obj.centerLongitude = gs.values[baseIndex + 2];
        obj.centerLatitude = gs.values[baseIndex + 3];
        obj.centerAltitude = gs.values[baseIndex + 4];
        obj.orientation = gs.values[baseIndex + 5];
        obj.width = gs.values[baseIndex + 6];
        obj.length = gs.values[baseIndex + 7];
        obj.height = gs.values[baseIndex + 8];
        obj.speed = gs.values[baseIndex + 9];
        sample.objects[i] = obj;
    }

    return sample;
}

function convGlobalObjectsSampleV2(gs) {
    if (gs.values.length < 1) return null;

    var objectCount = gs.values[0] == null ? 0 : gs.values[0];
    if (gs.values.length != 1 + 26 * objectCount) return null;

    var sample = new GlobalObjectsSample(gs.session, gs.time);
    sample.objects = Array(objectCount);

    for (var i = 0; i < objectCount; i++) {
        var obj = new DynamicObject();
        var baseIndex = 1 + 26 * i;
        obj.id = gs.values[baseIndex + 0];
        if (gs.values[baseIndex + 1] != null) obj.type = gs.values[baseIndex + 1];
        obj.centerLongitude = gs.values[baseIndex + 2];
        obj.centerLatitude = gs.values[baseIndex + 3];
        obj.centerAltitude = gs.values[baseIndex + 4];
        obj.orientation = gs.values[baseIndex + 5];
        obj.width = gs.values[baseIndex + 6];
        obj.length = gs.values[baseIndex + 7];
        obj.height = gs.values[baseIndex + 8];
        obj.speed = gs.values[baseIndex + 9];
        obj.accelX = gs.values[baseIndex + 10];
        obj.curvature = gs.values[baseIndex + 11];
        obj.yawRate = gs.values[baseIndex + 12];
        obj.pitch = gs.values[baseIndex + 13];
        obj.roll = gs.values[baseIndex + 14];
        obj.rearAxisLongitudinalOffset = gs.values[baseIndex + 15];
        obj.rearAxisHeightOffset = gs.values[baseIndex + 16];
        if (gs.values[baseIndex + 17] != null && gs.values[baseIndex + 18] != null && gs.values[baseIndex + 19] != null) {
            var color = new GlobalObjectColor();
            color.valid = true;
            color.r = gs.values[baseIndex + 17];
            color.g = gs.values[baseIndex + 18];
            color.b = gs.values[baseIndex + 19];
            obj.color = color;
        }
        sample.objects[i] = obj;
    }

    return sample;
}

function interpolateAngle(a1, w1, a2, w2) {
    if (a1 == null || a2 == null) return null;

    var deg2rad = Math.PI / 180;
    var x1 = Math.cos(a1 * deg2rad);
    var y1 = Math.sin(a1 * deg2rad);
    var x2 = Math.cos(a2 * deg2rad);
    var y2 = Math.sin(a2 * deg2rad);

    var xo = x1 * w1 + x2 * w2;
    var yo = y1 * w1 + y2 * w2;
    if (xo == 0 && yo == 0) return null;

    return Math.atan2(yo, xo) / deg2rad;
}

// Interpolator / 样本插值函数
function interpolateGlobalObjectsSample(session, time, s1, w1, s2, w2) {
    var sample = new GlobalObjectsSample(session, time);

    for (var i1 = 0; i1 < s1.objects.length; i1++) {
        var o1 = s1.objects[i1];
        var id = o1.id;
        for (var i2 = 0; i2 < s2.objects.length; i2++) {
            var o2 = s2.objects[i2];
            if (o2.id != id) continue;

            var obj = new DynamicObject();
            obj.id = id;
            obj.type = w1 > w2 ? o1.type : o2.type;
            obj.centerLongitude = o1.centerLongitude * w1 + o2.centerLongitude * w2;
            obj.centerLatitude = o1.centerLatitude * w1 + o2.centerLatitude * w2;
            obj.centerAltitude = o1.centerAltitude * w1 + o2.centerAltitude * w2;
            obj.orientation = interpolateAngle(o1.orientation, w1, o2.orientation, w2);
            obj.width = o1.width * w1 + o2.width * w2;
            obj.length = o1.length * w1 + o2.length * w2;
            obj.height = o1.height * w1 + o2.height * w2;
            obj.speed = o1.speed * w1 + o2.speed * w2;
            obj.accelX = o1.accelX * w1 + o2.accelX * w2;
            obj.curvature = o1.curvature * w1 + o2.curvature * w2;
            obj.yawRate = o1.yawRate * w1 + o2.yawRate * w2;
            obj.pitch = o1.pitch * w1 + o2.pitch * w2;
            obj.roll = o1.roll * w1 + o2.roll * w2;
            obj.rearAxisLongitudinalOffset = o1.rearAxisLongitudinalOffset * w1 + o2.rearAxisLongitudinalOffset * w2;
            obj.rearAxisHeightOffset = o1.rearAxisHeightOffset * w1 + o2.rearAxisHeightOffset * w2;
            obj.color = w1 > w2 ? o1.color : o2.color;
            sample.objects.push(obj);
            break;
        }
    }

    return sample;
}

// Supported protocols / 全局目标物样本支持解析的协议
var globalObjectsSampleProtocols = [
    "global-objects-sample-v1",
    "global-objects-sample-v2",
];

// Converters / 通用样本转换函数表
var globalObjectsSampleConverters = {
    "global-objects-sample-v1": convGlobalObjectsSampleV1,
    "global-objects-sample-v2": convGlobalObjectsSampleV2,
};

// Use this function to query sample at target time. If "interpolated" is true, the interpolated frame is preferred, otherwise the nearest frame / 获取指定session指定时刻的全局目标物样本，若interpolated为true则优先取插值帧，否则固定为最近帧
function getGlobalObjectsSample(session, time, interpolated) {
    var s1 = null, w1 = null, s2 = null, w2 = null;
    var pairV2 = biGetGeneralSamplePair('global-objects-sample-v2', null, session, time, 1);
    var pairV1 = biGetGeneralSamplePair('global-objects-sample-v1', null, session, time, 1);
    if (pairV2 != null) {
        if (pairV2.sample1 != null) s1 = convGlobalObjectsSampleV2(pairV2.sample1);
        if (pairV2.sample2 != null) s2 = convGlobalObjectsSampleV2(pairV2.sample2);
        w1 = pairV2.weight1;
        w2 = pairV2.weight2;
    }
    else if (pairV1 != null) {
        if (pairV1.sample1 != null) s1 = convGlobalObjectsSampleV1(pairV1.sample1);
        if (pairV1.sample2 != null) s2 = convGlobalObjectsSampleV1(pairV1.sample2);
        w1 = pairV1.weight1;
        w2 = pairV1.weight2;
    }
    else return null;
    if (s1 == null || s2 == null) {
        return s1 == null ? s2 : s1;
    }
    if (interpolated) {
        return interpolateGlobalObjectsSample(session, time, s1, w1, s2, w2);
    }
    else {
        return w1 > w2 ? s1 : s2;
    }
} 